/**
 * 装饰器
 * 
 * */

export { }

// class Person {
//     say() { }
// }
// // 等价于如下-----
// function Person() { }

// Object.defineProperty(Person.prototype, 'say', {
//     value: function say() { }
// })



// 类装饰器
function addNameEat(constructor: Function) {
    constructor.prototype.name = 'zhufeng';
    constructor.prototype.eat = function () { }

}
@addNameEat
class Person {
    name!: string;
    eat!: Function;
    constructor() { }
}

let p: Person = new Person();
console.log(p.name);
p.eat();



// 类装饰器工厂-目的是为了解决在装饰器中传参的问题
// ------引申内容：在一个文件中两个类重名时如何解决？
// 如果想在一个文件中声明两个同名的类时,可以是用命名空间(namespace)来解决命名冲突的问题。
// namespace a{
//     //包裹重名的类及方法
// }

namespace b {
    function addNameEatFactory(name: string) {
        // 在函数中返回该方法，将传参置入其中
        return function addNameEat(constructor: Function) { //在装饰器中,传参必须为构造函数
            constructor.prototype.name = name;
            constructor.prototype.eat = function () { };
        }
    }
    @addNameEatFactory('架构课') // 使用装饰器工厂即可以在装饰器中传参
    class Person {      // 外部的命名空间是为了分隔此处的Person类
        name!: string;
        eat!: Function;
        constructor() { }
    }
    let p: Person = new Person();
    console.log(p.name);
    p.eat()
}



// 类装饰器-替换类

namespace c {
    function replaceClass(constructor: Function) {
        // 在此处返回,由一个方法更换为一个类
        return class {
            name!: string;
            eat!: Function;  //ts原则,初始装饰器类属性可以比实例化属性多,但不能比初始类型少(基于ts的类型安全策略),当注释掉eat时,则会报错:在此处声明了 "eat"。
            age!: number
            constructor() { }
        }
    }
    @replaceClass
    class Person {
        name!: string;
        eat!: Function; // 此处也可以印证,实例化的Person类少了age属性，但并未报错
        constructor() { }
    }
}




// 属性装饰器